/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.niir.goodfil.ui;

import org.apache.log4j.Logger;
import ru.niir.goodfil.audit.domain.OrderUIEvent;
import ru.niir.goodfil.audit.domain.enums.OrderUIEventType;
import ru.niir.goodfil.db.*;
import ru.niir.goodfil.db.dic.OrderState;
import ru.niir.goodfil.exceptions.*;
import ru.niir.goodfil.ui.utils.RestsExportManager;
import ru.niir.goodfil.utils.Pair;
import ru.niir.goodfil.utils.filters.ClientsFilter;
import ru.niir.goodfil.utils.validation.CreateCheckResult;
import ru.niir.goodfil.utils.validation.ValidationError;
import ru.niir.goodfil.utils.validation.ValidationResult;

import javax.faces.model.SelectItem;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import static ru.niir.goodfil.ui.Utils.setReservationDate;
import static ru.niir.goodfil.utils.Utils._assert;

/**
 * Бин для работы со страницей заказов
 */
public class OrdersBean extends PageBean {
    private static Logger logger = Logger.getLogger(OrdersBean.class);

    /**
     * Наименование клиента (для фильтра)
     */
    private final SessionString clientName = SessionString("OrdersClientName");

    /**
     * Идентификатор заказа (для удаления)
     */
    private long orderId;

    /**
     * Для создания нового заказа
     */
    private long clientId = -1;

    private Order order = null;

    public OrdersBean() {
        //  Доступ на страницу заказов разрешен только администраторам и менеджерам
        if (!(getSecurityBean().getIsRoleAdministrator() || getSecurityBean().getIsRoleManager())) {
            throw new GoodfilSecurityException("Доступ на страницу заказов разрешен только администраторам и менеджерам");
        }
    }

    public Order getOrder() {
        if (order == null) {
            order = gf().getOrderById(orderId);

            if (order != null) {
                //  Чтобы менеджер не мог получить НЕ свой заказ
                if (!getSecurityBean().isManagerCorrect(order.getManagerId())) {
                    throw new GoodfilSecurityException();
                }

                order.setClient(gf().getClientById(order.getClientId()));
                order.setManager(gf().getUserById(order.getManagerId()));
            }
        }
        return order;
    }

    /**
     * Возвращает имя клиента.
     *
     * @return имя клиента
     */
    public String getClientName() {
        return clientName.getValue();
    }

    public String getSelectedClientName() {
        long clientId = getSessionBean().getOrdersFilter().getClientId();
        if (clientId > 0) {
            Client client = gf().getClientById(clientId);
            if (client != null) {
                return client.getCompanyName();
            }
        }
        return null;
    }

    /**
     * Устанавливает имя клиента.
     *
     * @param clientName имя клиента
     */
    public void setClientName(String clientName) {
        logger.debug("setClientName(" + clientName + ")");
        this.clientName.setValue(clientName);
    }


    public Client getSelectedClient() {
        long clientId = getSessionBean().getOrdersFilter().getClientId();
        if (clientId == 0 || clientId == 1) {
            return null;
        }
        return gf().getClientById(clientId);
    }

    /**
     * Возвращает идентификатор заказа.
     *
     * @return идентификатор заказа
     */
    public long getOrderId() {
        return orderId;
    }

    /**
     * Удаляет идентификатор заказа.
     *
     * @param orderId идентификатор заказа
     */
    public void setOrderId(long orderId) {
        this.orderId = orderId;
    }

    /**
     * Возвращает идентификатор клиента.
     *
     * @return идентификатор клиента
     */
    public long getClientId() {
        return clientId;
    }

    /**
     * Устанавливает идентификатор клиента.
     *
     * @param clientId идентификатор клиента
     */
    public void setClientId(long clientId) {
        this.clientId = clientId;
    }

    /**
     * Возвращает список клиентов.
     *
     * @return список клиентов
     */
    public List<Client> getClients() {
        List<Client> clients = new ArrayList<Client>();
        if (clientName.getValue().length() >= 3) {
            //  Если залогинен администратор - то фильтрация только по имени клиента,
            //  если залогинен менеджер - фильтрация по имени клиента и по принадлежности клиента данному менеджеру.
            //  Это нужно, чтобы менеджер мог видеть только своих клиентов.
            ClientsFilter clientsFilter = new ClientsFilter();
            clientsFilter.setName(clientName.getValue());
            clientsFilter.setManagerId(getSecurityBean().getIsRoleManager() ? getSecurityBean().getUserId() : -1);

            clients = gf().getClients(0, Integer.MAX_VALUE, clientsFilter);
            for (Client client : clients) {
                client.setUser(gf().getUserByClientId(client.getId()));
            }
        }
        return clients;
    }

    public List<Client> getAllClients() {
        //  Если залогинен администратор - то фильтрация только по имени клиента,
        //  если залогинен менеджер - фильтрация по имени клиента и по принадлежности клиента данному менеджеру.
        //  Это нужно, чтобы менеджер мог видеть только своих клиентов.
        ClientsFilter clientsFilter = new ClientsFilter();
        clientsFilter.setManagerId(getSecurityBean().getIsRoleManager() ? getSecurityBean().getUserId() : -1);

        List<Client> clients = gf().getClients(0, Integer.MAX_VALUE, clientsFilter);
        for (Client client : clients) {
            client.setUser(gf().getUserByClientId(client.getId()));
        }
        return clients;
    }

    public List<SelectItem> getAllClientsAsSelectItems() {
        List<SelectItem> result = new ArrayList<SelectItem>();
        result.add(new SelectItem(-1, "Не выбран"));
        for (Client client : getAllClients()) {
            result.add(new SelectItem(client.getId(), client.getCompanyName()));
        }
        return result;
    }

    /**
     * Возвращает список заказов.
     *
     * @return список заказов
     */
    public List<Order> getOrdersList() {
        if (getSessionBean().getOrdersFilter().getIsEmpty()) {
            return new ArrayList<Order>();
        }

        //  Здесь будут возвращены только заказы данного менеджера, так как при входе менеджера
        //  фильтр getSessionBean().getOrdersFilter() блокируется на изменение идентификатора менеджера 
        List<Order> orders = gf().getOrders(0, Integer.MAX_VALUE, getSessionBean().getOrdersFilter());
        for (Order o : orders) {
            o.setClient(gf().getClientById(o.getClientId()));
            o.setManager(gf().getUserById(o.getManagerId()));

            try {
                gf().addOrderInfo(o);
            } catch (SettingNotFoundException ex) {
                logger.error(ex);
                ex.printStackTrace();
                facesError(ex.getSettingName());
            }

            //  Дополняем заказ информацией о том, можно ли его удалять
            _assert(o != null);
            _assert(o.getProps() != null);
            if (getSecurityBean().getIsRoleAdministrator()) {
                o.getProps().put("deletable", "true");
            }
            if (getSecurityBean().getIsRoleManager()) {
               /* if(gf().getIsReservedOn()){
                    o.getProps().put("deletable", (o.getOrderIsEditableWithReserves() ? "true" : "false"));
                }
                else {
                    o.getProps().put("deletable", (o.getOrderIsEditable() ? "true" : "false"));
                }*/

                o.getProps().put("deletable", (o.getOrderIsEditableWithReserves() ? "true" : "false"));
            }

        }
        return orders;
    }

    /**
     * Возвращает список статусов заказов.
     *
     * @return список статусов заказов
     */
    public List<SelectItem> getOrderStates() {
        List<SelectItem> list = new ArrayList<SelectItem>();
        list.add(new SelectItem(-1, "Не выбрано"));
        for (Pair pair : OrderState.list()) {
            list.add(new SelectItem(pair.getId(), pair.getValue()));
        }
        return list;
    }

    /**
     * Возвращает список заменяемых менеджеров.
     *
     * @return спислк заменяемых менеджеров
     */
    public List<SelectItem> getManagersList() {
        List<SelectItem> result = new ArrayList<SelectItem>();
        result.add(new SelectItem(-1, "Не установлен"));
        for (User u : gf().getUsersByRoleId(Role.MANAGER)) {
            result.add(new SelectItem(u.getId(), u.getFio()));
        }
        return result;
    }


    /**
     * -----------------------------------------------------------------------------------Работа с БД----------
     * -------------------------------------------------------------------------
     */

    public void deleteOrder() {
        //  Разделение прав пользователей ------------------------------------------------------
        //
        //  Удалить заказ может менеджер и администратор.
        //  Кладовщик удалить заказ не может. Менеджер может удалить только свой заказ.
        //

        //  Кладовщик удалить заказ не может.
        if (getSecurityBean().getIsRoleStoreman()) {
            throw new GoodfilSecurityException();
        }

        //  Менеджер может удалить только свой заказ.
        if (!getSecurityBean().isManagerCorrect(gf().getOrderById(orderId).getManagerId())) {
            throw new GoodfilSecurityException();
        }

        if(getSecurityBean().getIsRoleManager() && gf().getIsReservedOn() && !gf().getOrderById(orderId).getOrderIsEditableWithReserves()){
            facesError("Заказ принят в работу складом. Невозможно удалить заказ");
            return;
        }
        //  -------------------------------------------------------------------------------------

        logger.debug("Deleting order: " + orderId);

        try {
            List<StorageDeliveryStatus> deliveryStatusList = gf().getStorageDeliveryStatusByOrderId(orderId);
            if(deliveryStatusList != null && deliveryStatusList.size() != 0){
                for(StorageDeliveryStatus status : deliveryStatusList){
                    try{
                        gf().removeStorageDelviveryStatus(status.getId());}
                    catch (Exception e){
                        logger.error(e.getMessage());
                    }
                }
            }
            try{
                gf().softRemoveOrder(orderId);
            }catch (IllegalArgumentException e){
                facesError("Произошла ошибка. Пожалуйста, попробуйте позже или свяжитесь с администратором. Текст ошибки: " + e.getMessage());
                logger.error(e.getMessage());
            }catch (ValidationException e){
                facesError("Произошла ошибка. Пожалуйста, попробуйте позже или свяжитесь с администратором. Текст ошибки: " + e.getMessage());
                logger.error(e.getMessage());
            }

            if(gf().getOrderById(orderId).getOrderStateIsOutput() && gf().getOrderById(orderId).getDeliveryDate() != null || gf().getOrderById(orderId).getOrderStateIsOnDelivery()){
                RestsExportManager.sendOrderRemoveMessageForStoreKeeper(gf(), orderId);
            }
            facesInfo("Заказ удален");
        } catch (Exception e) {
            logger.error(e.getMessage());
            facesError("Не удалось удалить заказ");
        }
    }

    /**
     * Создание нового заказа.
     */
    public void newOrder() {

        //  Проверяем, что заказ может быть создан с учетом механизма товарного кредите
        if (!gf().clientCanSaveOrder(clientId)) {
            facesError("Долг данного клиента не допускает оформления заказа за него");
            return;
        }
        //  ----------------------------------------------------------------------------------------

        getSessionBean().resetNewOrderFilters();


        Client c = gf().getClientById(clientId);

        //  Разделение прав пользователей -----------------------------------------------------------
        //  Создать заказ может менеджер и администратор. Кладовщик создать заказ не может.
        //  Менеджер может создать заказ только для своего клиента
        //

        //  Кладовщик создать заказ не может.
        if (getSecurityBean().getIsRoleStoreman()) {
            throw new GoodfilSecurityException();
        }

        //  Менеджер может создать заказ только для своего клиента.
        //  EXPERIMENTAL: или за подчиненного
        if (!getSecurityBean().isManagerCorrect(c.getManagerId())) {
            throw new GoodfilSecurityException();
        }

        //  -----------------------------------------------------------------------------------------

        Order theOrder = new Order();
        setReservationDate(theOrder, gf());
//          Это теперь не нужно, статус PRE_SAVED устанавливается по-умолчанию
//            order.setOrderStateId(OrderState.PRE_SAVED);
        theOrder.setClientId(clientId);
        if(gf().getIsReservedOn()){
            theOrder.setOrderStateId(OrderState.AWAITING_VALIDATION);
        }

        theOrder.setManagerId(c.getManagerId());

        //  Информация о доставке
        c.getDeliveryInfoExt().copyTo(theOrder.getDeliveryInfoExt());

        gf().createIfNotExists("deliveryTimeSpan", 7);
        int deliveryTimeSpan = gf().getInt("deliveryTimeSpan");   //  Не помню что это такое

        Calendar deliveryDate = Calendar.getInstance();
        deliveryDate.add(Calendar.DAY_OF_MONTH, theOrder.getDeliveryInfoExt().getPeriod());
        theOrder.setDeliveryDate(deliveryDate.getTime());

        theOrder.setDeliveryTypeId(c.getDeliveryTypeId());         //  Deprecated
        theOrder.setDeliveryAddress(c.getAddressDlv());            //  Deprecated
        theOrder.setDeliveryTimeSpan(deliveryTimeSpan);            //  Deprecated
        theOrder.setDeliverySum(0);                                //  Тут наверное надо определять реальную стоимость доставки?
        try {
            gf().saveOrder(theOrder);
            facesInfo("Заказ сохранен");
        } catch (ValidationException ex) {
            logger.error(ex);
            ex.printStackTrace();
            ValidationResult validationResult = ex.getResult();
            for (ValidationError error : validationResult.getErrors()) {
                facesError(error.toHumanString());
            }
        } catch (CreateFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            CreateCheckResult createCheckResult = ex.getResult();
            facesError(createCheckResult.getMsg());
        }

        gf().addOrderInfo(theOrder);
        getAuditService().logOrderUIEvent(prepareOrderUIEvent(OrderUIEventType.CREATED, theOrder));

        //  Установка значений в сессионный бин
        getSessionBean().setSelectedOrderId(theOrder.getId());
        getSessionBean().setSelectedClientId(c.getId());
        getSessionBean().setOrder(theOrder);
        try {
            getSessionBean().setOrderItems(new ArrayList<OrderItem>());
        } catch (RemoveFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            String[] objs = ex.getRemoveCheckResult().getLinkedObjects();
            for (String obj : objs) {
                facesError("Заказ нельзя удалить, т.к. он связан с другими объектами.");
                break;
            }
        } catch (CreateFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            CreateCheckResult createCheckResult = ex.getResult();
            facesError(createCheckResult.getMsg());
        } catch (ValidationException ex) {
            logger.error(ex);
            ex.printStackTrace();
            ValidationResult validationResult = ex.getResult();
            for (ValidationError error : validationResult.getErrors()) {
                facesError(error.toHumanString());
            }
        } catch (SettingNotFoundException ex) {
            logger.error(ex);
            ex.printStackTrace();
            facesError(ex.getSettingName());
        }
    }

    public void resetClientName() {
        logger.debug("resetClientName()");
        clientName.clear();
        getSessionBean().getOrdersFilter().setClientId(-1);
    }

    private OrderUIEvent prepareOrderUIEvent(OrderUIEventType type, Order order) {
        OrderUIEvent event = new OrderUIEvent();

        event.setIp(getAuditHelper().getIp());
        event.setLogin(getAuditHelper().getLogin());
        event.setType(type);

        event.setOrderId(String.valueOf(order.getId()));
        event.setC1Num(order.getC1Num());

        Client client = order.getClient();
        if (client == null) client = gf().getClientById(order.getClientId());
        event.setClientId(String.valueOf(client.getId()));
        event.setClientName(client.getCompanyName());

        User manager = order.getManager();
        if (manager == null) manager = gf().getUserById(order.getManagerId());
        event.setManagerId(String.valueOf(manager.getId()));
        event.setManagerName(manager.getFio());

        event.setCreationDate(order.getCreationDateAsString());
        event.setDeliveryDate(order.getDeliveryDateAsString());
        event.setOrderSum(String.valueOf(order.getSum()));

        return event;
    }


}
